// i.d(t, { d: () => ObservableArray, C: () => r })
import Observable from "./observable"
import funcUtil from "../utils/func.util"

export default class ObservableArray<T = any> extends Observable {
  isObservableArray: boolean
  elements: T[]
  constructor() {
    super()
    this.isObservableArray = !0
    this.elements = []
  }
  push(e: T) {
    this.elements.push(e)
    this.addChildObservable(e)
    this.setDirty()
    return this.length
  }
  concat(e: T | T[]) {
    const t = this.elements.length
    this.elements = this.elements.concat(e)
    for (let e = t; e < this.elements.length; e++) this.addChildObservable(this.elements[e])
    return this.setDirty(), this.length
  }
  replace(e: T | T[]) {
    for (const e of this.elements) this.removeChildObservables(e)
    return (this.elements = []), this.concat(e)
  }
  remove(e: number) {
    if (e < 0 || e >= this.length) throw new Error(`Index ${e} not in range of ObservableArray`)
    const [t] = this.elements.splice(e, 1)
    this.removeChildObservables(t), this.setDirty()
  }
  update(e: number, t: T) {
    const i = this.elements[e]
    i !== t && ((this.elements[e] = t), this.removeChildObservables(i), this.addChildObservable(t), this.setDirty())
  }
  get length() {
    return this.elements.length
  }
  set length(e) {
    if (e < this.elements.length) {
      for (let t = this.elements.length - 1; t >= e; t--) {
        const e = this.elements[t]
        this.removeChildObservables(e)
      }
      ;(this.elements.length = e), this.setDirty()
    }
  }
  values() {
    return [...this.elements]
  }
  filter(e: (value: T, index: number, array: T[]) => any) {
    return this.elements.filter(e)
  }
  find(e: (value: T, index: number, array: T[]) => any) {
    return this.elements.find(e)
  }
  *[Symbol.iterator]() {
    for (const e of this.elements) yield e
  }
  map(e: (value: T, index: number, array: T[]) => any) {
    return this.elements.map(e)
  }
  forEach(e: (val?: T, index?: number, data?: T[]) => void) {
    this.elements.forEach(e)
  }
  sort(e: (a: T, b: T) => number) {
    this.elements.sort(e)
  }
  get(e: number) {
    return this.elements[e]
  }
  findIndex(e: (value: T, index: number, obj: T[]) => any) {
    return this.elements.findIndex(e)
  }
  indexOf(e: T) {
    return this.elements.indexOf(e)
  }
  splice(e: number, t: number, ...i: T[]) {
    let n
    if (i.length) {
      n = this.elements.splice(e, t, ...i)
      for (const e of i) this.addChildObservable(e)
    } else n = this.elements.splice(e, t)
    for (const e of n) this.removeChildObservables(e)
    return this.setDirty(), n
  }
  insert(e: number, t: T) {
    if (e > this.elements.length) throw new Error(`Tried to insert at index ${e} in an array that's only ${this.elements.length} long`)
    e !== this.elements.length ? (this.elements.splice(e, 0, t), this.addChildObservable(t), this.setDirty()) : this.push(t)
  }
  shift() {
    const e = this.elements.shift()
    return e && (this.removeChildObservables(e), this.setDirty()), e
  }
  move(from: number, to: number) {
    if (to > this.elements.length - 1) throw Error("Index to move to is out of bounds")
    const i = this.elements[from]
    this.elements.splice(from, 1)
    this.elements.splice(to, 0, i)
    this.setDirty()
  }
  onElementChanged(e) {
    let t = this.elements.slice()
    return this.onChanged(() => {
      for (const i in t) {
        const n = parseInt(i, 10)
        e.onRemoved && void 0 === this.get(n) && e.onRemoved(t[n], n)
      }
      for (const i in this.elements) {
        const s = parseInt(i, 10),
          r = this.get(s)
        void 0 === t[s]
          ? e.onAdded && e.onAdded(r, s)
          : ((Observable.isObservable(r) && r["_dirtyObservable"]) || r !== t[s]) &&
            (e.onUpdated ? e.onUpdated(r, s) : e.onRemoved && null === r && e.onRemoved(t[s], s))
      }
      t = this.elements.slice()
    })
  }
  deepCopy() {
    return this.map(e => funcUtil.deepCopy(e))
  }
}
export const createArray = e => {
  const t = new ObservableArray()
  e && t.concat(e)
  return t
}
